package com.chenju.invokechainservice.service.impl;

import com.chenju.invokechainservice.common.enums.ChangeType;
import com.chenju.invokechainservice.common.enums.FocusType;
import com.chenju.invokechainservice.common.exceptions.FunnyFileNotFoundException;
import com.chenju.invokechainservice.component.CodeExecutor;
import com.chenju.invokechainservice.domain.DiffMethodEntity;
import com.chenju.invokechainservice.domain.ImpactEntity;
import com.chenju.invokechainservice.domain.TInterfaceNode;
import com.chenju.invokechainservice.service.FunnyTestService;
import com.chenju.invokechainservice.service.JavaParserService;
import com.chenju.invokechainservice.service.TClassNodeService;
import com.chenju.invokechainservice.service.TInterfaceNodeService;
import com.chenju.invokechainservice.utils.FunnyStringUtils;
import org.checkerframework.checker.units.qual.C;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class FunnyTestServiceImplement implements FunnyTestService {
    private final static Logger logger = LoggerFactory.getLogger(FunnyTestServiceImplement.class);

    private final JavaParserService javaParserService;
    private final TClassNodeService classNodeService;
    private final TInterfaceNodeService interfaceNodeService;
    private final CodeExecutor executor;

    public FunnyTestServiceImplement(JavaParserService javaParserService, TClassNodeService classNodeService, TInterfaceNodeService interfaceNodeService, CodeExecutor executor) {
        this.javaParserService = javaParserService;
        this.classNodeService = classNodeService;
        this.interfaceNodeService = interfaceNodeService;
        this.executor = executor;
    }

    @Override
    public void codePrepare(String rootPath, String gitUrl, String oldCommitId, String oldCompileEnv, String newCommitId, String newCompileEnv) {
        String oldCodePath = FunnyStringUtils.joinPath(rootPath, "master");
        String newCodePath = FunnyStringUtils.joinPath(rootPath, "feature");
        logger.info(String.format(
                "旧分支代码存放路径: %s\n" +
                "新分支代码存放路径: %s", oldCodePath, newCodePath
        ));
        // 拉取两个分支代码，旧的存在master，新的存在feature
        logger.info("====================开始克隆并编译新旧分支代码！====================");
        executor.cloneCode(gitUrl, oldCodePath, oldCommitId);
        executor.compileCode(oldCodePath, oldCompileEnv);
        executor.cloneCode(gitUrl, newCodePath, newCommitId);
        executor.compileCode(newCodePath, newCompileEnv);
        logger.info("====================克隆并编译新旧分支代码完成！====================");
    }

    @Override
    public List<String> impactScopeAnalysis(String rootPath) throws IOException {
        // 计算代码路径
        String oldCodePath = FunnyStringUtils.joinPath(rootPath, "master");
        String newCodePath = FunnyStringUtils.joinPath(rootPath, "feature");

        // 解析Java代码的Diff
        List<DiffMethodEntity> diffMethods = javaParserService.parseProjectDiffMethod(rootPath);
        Map<ChangeType, List<DiffMethodEntity>> diffMethodsMap = diffMethods.stream().collect(Collectors.groupingBy(DiffMethodEntity::getChangeType));
        List<DiffMethodEntity> addMethods = diffMethodsMap.get(ChangeType.ADD)!=null ? diffMethodsMap.get(ChangeType.ADD) : new ArrayList<>();
        List<DiffMethodEntity> modifyMethods = diffMethodsMap.get(ChangeType.MODIFY)!=null ? diffMethodsMap.get(ChangeType.MODIFY) : new ArrayList<>();
        List<DiffMethodEntity> deleteMethods = diffMethodsMap.get(ChangeType.DELETE)!=null ? diffMethodsMap.get(ChangeType.DELETE) : new ArrayList<>();
        logger.info(String.format("已获取路径【%s】与路径【%s】中所有Java文件的Diff结果！", oldCodePath, newCodePath));
        if (!addMethods.isEmpty()){
            logger.info("----------新增的方法名称如下(名称 | 签名)----------");
            addMethods.forEach(a -> logger.debug(String.format("%s | %s", a.getNewMethod().getNameAsString(),  a.getNewMethod().getSignature().asString())));
        }
        if (!modifyMethods.isEmpty()){
            logger.info("----------修改的方法名称如下(名称 | 签名)----------");
            modifyMethods.forEach(a -> logger.debug(String.format("%s | %s", a.getNewMethod().getNameAsString(),  a.getNewMethod().getSignature().asString())));
        }
        if (!deleteMethods.isEmpty()){
            logger.info("----------删除的方法名称如下(名称 | 签名)----------");
            deleteMethods.forEach(a -> logger.debug(String.format("%s | %s", a.getOldMethod().getNameAsString(),  a.getOldMethod().getSignature().asString())));
        }

        List<String> allImpactInterfaces = new ArrayList<>();

        logger.info("====================开始解析旧分支的Neo4J图！====================");
        // 解析class文件，生成neo4j的图
        // 旧的图重点关注MODIFY，次重点关注DELETE，不关注ADD
        classNodeService.parseProjectToNode(oldCodePath);
        logger.info("====================解析旧分支的Neo4J图结束！====================");
        for (int j=0; j<deleteMethods.size(); j++){
            // methodName, ChangeType, [api1, api2], FocusType
            // {api1: {methods: [{methodName: methodName, changeType: ChangeType}]}, FocusType: FocusType}, api2: {}}
            allImpactInterfaces.addAll(interfaceNodeService.findAllImpactInterfaceNodeByMethodName(deleteMethods.get(j).getOldMethod().getNameAsString()));
        }
        for (int i=0; i<modifyMethods.size(); i++){
            allImpactInterfaces.addAll(interfaceNodeService.findAllImpactInterfaceNodeByMethodName(modifyMethods.get(i).getOldMethod().getNameAsString()));
        }

        logger.info("====================开始解析新分支的Neo4J图！====================");
        // 新的图重点关注ADD， 次重点关注MODIFY，不关注DELETE
        classNodeService.parseProjectToNode(newCodePath);
        logger.info("====================解析新分支的Neo4J图结束！====================");
        for (int i=0; i<modifyMethods.size(); i++){
            allImpactInterfaces.addAll(interfaceNodeService.findAllImpactInterfaceNodeByMethodName(modifyMethods.get(i).getOldMethod().getNameAsString()));
        }
        for (int j=0; j<addMethods.size(); j++){
            allImpactInterfaces.addAll(interfaceNodeService.findAllImpactInterfaceNodeByMethodName(addMethods.get(j).getNewMethod().getNameAsString()));
        }

        return allImpactInterfaces.stream().distinct().collect(Collectors.toList());
    }

    @Override
    public Map<String, Map<String, Object>> impactScopeAnalysis2(String rootPath) throws IOException {
        // 计算代码路径
        String oldCodePath = FunnyStringUtils.joinPath(rootPath, "master");
        String newCodePath = FunnyStringUtils.joinPath(rootPath, "feature");

        // 解析Java代码的Diff
        List<DiffMethodEntity> diffMethods = javaParserService.parseProjectDiffMethod(rootPath);
        Map<ChangeType, List<DiffMethodEntity>> diffMethodsMap = diffMethods.stream().collect(Collectors.groupingBy(DiffMethodEntity::getChangeType));
        List<DiffMethodEntity> addMethods = diffMethodsMap.get(ChangeType.ADD)!=null ? diffMethodsMap.get(ChangeType.ADD) : new ArrayList<>();
        List<DiffMethodEntity> modifyMethods = diffMethodsMap.get(ChangeType.MODIFY)!=null ? diffMethodsMap.get(ChangeType.MODIFY) : new ArrayList<>();
        List<DiffMethodEntity> deleteMethods = diffMethodsMap.get(ChangeType.DELETE)!=null ? diffMethodsMap.get(ChangeType.DELETE) : new ArrayList<>();
        logger.info(String.format("已获取路径【%s】与路径【%s】中所有Java文件的Diff结果！", oldCodePath, newCodePath));
        if (!addMethods.isEmpty()){
            logger.debug("----------新增的方法名称如下(名称 | 签名)----------");
            addMethods.forEach(a -> logger.debug(String.format("%s | %s", a.getNewMethod().getNameAsString(),  a.getNewMethod().getSignature().asString())));
        }
        if (!modifyMethods.isEmpty()){
            logger.debug("----------修改的方法名称如下(名称 | 签名)----------");
            addMethods.forEach(a -> logger.debug(String.format("%s | %s", a.getNewMethod().getNameAsString(),  a.getNewMethod().getSignature().asString())));
        }
        if (!deleteMethods.isEmpty()){
            logger.debug("----------删除的方法名称如下(名称 | 签名)----------");
            addMethods.forEach(a -> logger.debug(String.format("%s | %s", a.getOldMethod().getNameAsString(),  a.getOldMethod().getSignature().asString())));
        }
        // 尝试一种很新的东西^_^
        Map<String, Map<String, Object>> map = new HashMap<>();
        classNodeService.parseProjectToNode(oldCodePath);
        for (DiffMethodEntity method : deleteMethods){
            String methodName = method.getOldMethod().getNameAsString();
            List<String> deleteMethodImpactInterfaceNames = interfaceNodeService.findAllImpactInterfaceNodeByMethodName(methodName);
            for (String interfaceName : deleteMethodImpactInterfaceNames){
                if (map.get(interfaceName) == null){
                    Map<String, Object> interfaceMap = new HashMap<>();
                    List<Map<String, Object>> methods = new ArrayList<>();
                    Map<String, Object> methodMap = new HashMap<>();
                    methodMap.put("methodName", methodName);
                    methodMap.put("changeType", ChangeType.DELETE);
                    methods.add(methodMap);
                    interfaceMap.put("methods", methods);
                    interfaceMap.put("FocusType", FocusType.SECONDARY);
                    map.put(interfaceName, interfaceMap);
                } else {
                    Map<String, Object> interfaceMap = map.get(interfaceName);
                    List<Map<String, Object>> methods = (List<Map<String, Object>>) interfaceMap.get("methods");
                    Map<String, Object> methodMap = new HashMap<>();
                    methodMap.put("methodName", methodName);
                    methodMap.put("changeType", ChangeType.DELETE);
                    if (map.get(interfaceName).get("methods") instanceof List && !((List<?>) map.get(interfaceName).get("methods")).contains(methodMap)){
                        methods.add(methodMap);
                        interfaceMap.put("methods", methods);
                    }
                    if (map.get(interfaceName).get("focusType") == FocusType.GENERAL){
                        interfaceMap.put("focusType", FocusType.SECONDARY);
                    }
                }

            }

        }
        for (DiffMethodEntity method : modifyMethods){
            String methodName = method.getOldMethod().getNameAsString();
            List<String> deleteMethodImpactInterfaceNames = interfaceNodeService.findAllImpactInterfaceNodeByMethodName(methodName);
            for (String interfaceName : deleteMethodImpactInterfaceNames){
                if (map.get(interfaceName) == null){
                    Map<String, Object> interfaceMap = new HashMap<>();
                    List<Map<String, Object>> methods = new ArrayList<>();
                    Map<String, Object> methodMap = new HashMap<>();
                    methodMap.put("methodName", methodName);
                    methodMap.put("changeType", ChangeType.DELETE);
                    methods.add(methodMap);
                    interfaceMap.put("methods", methods);
                    interfaceMap.put("FocusType", FocusType.PRIMARY);
                    map.put(interfaceName, interfaceMap);
                } else {
                    Map<String, Object> interfaceMap = map.get(interfaceName);
                    List<Map<String, Object>> methods = (List<Map<String, Object>>) interfaceMap.get("methods");
                    Map<String, Object> methodMap = new HashMap<>();
                    methodMap.put("methodName", methodName);
                    methodMap.put("changeType", ChangeType.DELETE);
                    if (map.get(interfaceName).get("methods") instanceof List && !((List<?>) map.get(interfaceName).get("methods")).contains(methodMap)){
                        methods.add(methodMap);
                        interfaceMap.put("methods", methods);
                    }
                    if (map.get(interfaceName).get("focusType") == FocusType.GENERAL || map.get(interfaceName).get("focusType") == FocusType.SECONDARY){
                        interfaceMap.put("focusType", FocusType.PRIMARY);
                    }
                }

            }

        }

        classNodeService.parseProjectToNode(newCodePath);
        for (DiffMethodEntity method : modifyMethods){
            String methodName = method.getOldMethod().getNameAsString();
            List<String> deleteMethodImpactInterfaceNames = interfaceNodeService.findAllImpactInterfaceNodeByMethodName(methodName);
            for (String interfaceName : deleteMethodImpactInterfaceNames){
                if (map.get(interfaceName) == null){
                    Map<String, Object> interfaceMap = new HashMap<>();
                    List<Map<String, Object>> methods = new ArrayList<>();
                    Map<String, Object> methodMap = new HashMap<>();
                    methodMap.put("methodName", methodName);
                    methodMap.put("changeType", ChangeType.DELETE);
                    methods.add(methodMap);
                    interfaceMap.put("methods", methods);
                    interfaceMap.put("FocusType", FocusType.SECONDARY);
                    map.put(interfaceName, interfaceMap);
                } else {
                    Map<String, Object> interfaceMap = map.get(interfaceName);
                    List<Map<String, Object>> methods = (List<Map<String, Object>>) interfaceMap.get("methods");
                    Map<String, Object> methodMap = new HashMap<>();
                    methodMap.put("methodName", methodName);
                    methodMap.put("changeType", ChangeType.DELETE);
                    if (map.get(interfaceName).get("methods") instanceof List && !((List<?>) map.get(interfaceName).get("methods")).contains(methodMap)){
                        methods.add(methodMap);
                        interfaceMap.put("methods", methods);
                    }
                    if (map.get(interfaceName).get("focusType") == FocusType.GENERAL){
                        interfaceMap.put("focusType", FocusType.SECONDARY);
                    }
                }

            }

        }
        for (DiffMethodEntity method : addMethods){
            String methodName = method.getNewMethod().getNameAsString();
            List<String> deleteMethodImpactInterfaceNames = interfaceNodeService.findAllImpactInterfaceNodeByMethodName(methodName);
            for (String interfaceName : deleteMethodImpactInterfaceNames){
                if (map.get(interfaceName) == null){
                    Map<String, Object> interfaceMap = new HashMap<>();
                    List<Map<String, Object>> methods = new ArrayList<>();
                    Map<String, Object> methodMap = new HashMap<>();
                    methodMap.put("methodName", methodName);
                    methodMap.put("changeType", ChangeType.DELETE);
                    methods.add(methodMap);
                    interfaceMap.put("methods", methods);
                    interfaceMap.put("FocusType", FocusType.PRIMARY);
                    map.put(interfaceName, interfaceMap);
                } else {
                    Map<String, Object> interfaceMap = map.get(interfaceName);
                    List<Map<String, Object>> methods = (List<Map<String, Object>>) interfaceMap.get("methods");
                    Map<String, Object> methodMap = new HashMap<>();
                    methodMap.put("methodName", methodName);
                    methodMap.put("changeType", ChangeType.DELETE);
                    if (map.get(interfaceName).get("methods") instanceof List && !((List<?>) map.get(interfaceName).get("methods")).contains(methodMap)){
                        methods.add(methodMap);
                        interfaceMap.put("methods", methods);
                    }
                    if (map.get(interfaceName).get("focusType") == FocusType.GENERAL || map.get(interfaceName).get("focusType") == FocusType.SECONDARY){
                        interfaceMap.put("focusType", FocusType.PRIMARY);
                    }
                }
            }
        }
        return map;
    }
}
